package fairygui.editor.pack
{
   public class MaxRects
   {
      
      public static const BestShortSideFit:int = 0;
      
      public static const BestLongSideFit:int = 1;
      
      public static const BestAreaFit:int = 2;
      
      public static const BottomLeftRule:int = 3;
      
      public static const ContactPointRule:int = 4;
      
      public static const AllMethods:Array = [0,1,2];
      
      private static var helperRect:NodeRect = new NodeRect();
       
      
      private var binWidth:int;
      
      private var binHeight:int;
      
      private var allowRotations:Boolean;
      
      private var usedRectangles:Vector.<NodeRect>;
      
      private var freeRectangles:Vector.<NodeRect>;
      
      public function MaxRects()
      {
         super();
         this.usedRectangles = new Vector.<NodeRect>();
         this.freeRectangles = new Vector.<NodeRect>();
      }
      
      public function init(param1:int, param2:int, param3:Boolean = false) : void
      {
         this.binWidth = param1;
         this.binHeight = param2;
         this.allowRotations = param3;
         var _loc4_:NodeRect = new NodeRect();
         _loc4_.x = 0;
         _loc4_.y = 0;
         _loc4_.width = param1;
         _loc4_.height = param2;
         this.usedRectangles.length = 0;
         this.freeRectangles.length = 0;
         this.freeRectangles.push(_loc4_);
      }
      
      public function insert(param1:NodeRect, param2:int) : NodeRect
      {
         var _loc3_:NodeRect = this.scoreRect(param1,param2);
         if(_loc3_.height == 0)
         {
            return null;
         }
         var _loc4_:NodeRect = new NodeRect();
         _loc4_.copyFrom(_loc3_);
         this.placeRect(_loc4_);
         return _loc4_;
      }
      
      public function pack(param1:Vector.<NodeRect>, param2:int) : Page
      {
         var _loc3_:int = 0;
         var _loc4_:NodeRect = null;
         var _loc5_:NodeRect = null;
         var _loc8_:int = 0;
         var _loc9_:NodeRect = null;
         var _loc6_:Vector.<NodeRect> = param1.concat();
         var _loc7_:int = _loc6_.length;
         while(_loc7_ > 0)
         {
            _loc3_ = -1;
            _loc4_ = new NodeRect();
            _loc4_.score1 = int.MAX_VALUE;
            _loc4_.score2 = int.MAX_VALUE;
            _loc8_ = 0;
            while(_loc8_ < _loc7_)
            {
               _loc9_ = _loc6_[_loc8_];
               _loc5_ = this.scoreRect(_loc9_,param2);
               if(_loc5_.score1 < _loc4_.score1 || _loc5_.score1 == _loc4_.score1 && _loc5_.score2 < _loc4_.score2)
               {
                  _loc4_.copyFrom(_loc5_);
                  _loc3_ = _loc8_;
               }
               _loc8_++;
            }
            if(_loc3_ == -1)
            {
               break;
            }
            this.placeRect(_loc4_);
            _loc6_.splice(_loc3_,1);
            _loc7_--;
         }
         var _loc10_:Page = this.getResult();
         _loc10_.remainingRects = _loc6_;
         return _loc10_;
      }
      
      public function getResult() : Page
      {
         var _loc3_:int = 0;
         var _loc6_:NodeRect = null;
         var _loc1_:int = 0;
         var _loc2_:int = 0;
         var _loc4_:int = this.usedRectangles.length;
         _loc3_ = 0;
         while(_loc3_ < _loc4_)
         {
            _loc6_ = this.usedRectangles[_loc3_];
            _loc1_ = Math.max(_loc1_,_loc6_.x + _loc6_.width);
            _loc2_ = Math.max(_loc2_,_loc6_.y + _loc6_.height);
            _loc3_++;
         }
         var _loc5_:Page = new Page();
         _loc5_.outputRects = this.usedRectangles.concat();
         _loc5_.occupancy = this.getOccupancy();
         _loc5_.width = _loc1_;
         _loc5_.height = _loc2_;
         return _loc5_;
      }
      
      private function getOccupancy() : Number
      {
         var _loc2_:int = 0;
         var _loc1_:int = 0;
         var _loc3_:int = this.usedRectangles.length;
         _loc2_ = 0;
         while(_loc2_ < _loc3_)
         {
            _loc1_ = _loc1_ + this.usedRectangles[_loc2_].width * this.usedRectangles[_loc2_].height;
            _loc2_++;
         }
         return _loc1_ / (this.binWidth * this.binHeight);
      }
      
      private function placeRect(param1:NodeRect) : void
      {
         var _loc2_:int = this.freeRectangles.length;
         var _loc3_:int = 0;
         while(_loc3_ < _loc2_)
         {
            if(this.splitFreeNode(this.freeRectangles[_loc3_],param1))
            {
               this.freeRectangles.splice(_loc3_,1);
               _loc3_--;
               _loc2_--;
            }
            _loc3_++;
         }
         this.pruneFreeList();
         this.usedRectangles.push(param1);
      }
      
      private function scoreRect(param1:NodeRect, param2:int) : NodeRect
      {
         var _loc5_:NodeRect = null;
         var _loc3_:int = param1.width;
         var _loc4_:int = param1.height;
         helperRect.height = 0;
         switch(param2)
         {
            case BestShortSideFit:
               _loc5_ = this.findPositionForNewNodeBestShortSideFit(_loc3_,_loc4_);
               break;
            case BottomLeftRule:
               _loc5_ = this.findPositionForNewNodeBottomLeft(_loc3_,_loc4_);
               break;
            case ContactPointRule:
               _loc5_ = this.findPositionForNewNodeContactPoint(_loc3_,_loc4_);
               _loc5_.score1 = -_loc5_.score1;
               break;
            case BestLongSideFit:
               _loc5_ = this.findPositionForNewNodeBestLongSideFit(_loc3_,_loc4_);
               break;
            case BestAreaFit:
               _loc5_ = this.findPositionForNewNodeBestAreaFit(_loc3_,_loc4_);
         }
         if(_loc5_.height == 0)
         {
            _loc5_.score1 = int.MAX_VALUE;
            _loc5_.score2 = int.MAX_VALUE;
         }
         _loc5_.id = param1.id;
         return _loc5_;
      }
      
      private function occupancy() : Number
      {
         var _loc1_:Number = 0;
         var _loc2_:int = this.usedRectangles.length;
         var _loc3_:int = 0;
         while(_loc3_ < _loc2_)
         {
            _loc1_ = _loc1_ + this.usedRectangles[_loc3_].width * this.usedRectangles[_loc3_].height;
            _loc3_++;
         }
         return _loc1_ / (this.binWidth * this.binHeight);
      }
      
      private function findPositionForNewNodeBottomLeft(param1:int, param2:int) : NodeRect
      {
         var _loc4_:NodeRect = null;
         var _loc5_:int = 0;
         var _loc3_:NodeRect = helperRect;
         _loc3_.score1 = int.MAX_VALUE;
         var _loc6_:int = this.freeRectangles.length;
         var _loc7_:int = 0;
         while(_loc7_ < _loc6_)
         {
            _loc4_ = this.freeRectangles[_loc7_];
            if(_loc4_.width >= param1 && _loc4_.height >= param2)
            {
               _loc5_ = _loc4_.y + param2;
               if(_loc5_ < _loc3_.score1 || _loc5_ == _loc3_.score1 && _loc4_.x < _loc3_.score2)
               {
                  _loc3_.x = _loc4_.x;
                  _loc3_.y = _loc4_.y;
                  _loc3_.width = param1;
                  _loc3_.height = param2;
                  _loc3_.score1 = _loc5_;
                  _loc3_.score2 = _loc4_.x;
                  _loc3_.rotated = false;
               }
            }
            if(this.allowRotations && _loc4_.width >= param2 && _loc4_.height >= param1)
            {
               _loc5_ = _loc4_.y + param1;
               if(_loc5_ < _loc3_.score1 || _loc5_ == _loc3_.score1 && _loc4_.x < _loc3_.score2)
               {
                  _loc3_.x = _loc4_.x;
                  _loc3_.y = _loc4_.y;
                  _loc3_.width = param2;
                  _loc3_.height = param1;
                  _loc3_.score1 = _loc5_;
                  _loc3_.score2 = _loc4_.x;
                  _loc3_.rotated = true;
               }
            }
            _loc7_++;
         }
         return _loc3_;
      }
      
      private function findPositionForNewNodeBestShortSideFit(param1:int, param2:int) : NodeRect
      {
         var _loc4_:NodeRect = null;
         var _loc5_:int = 0;
         var _loc6_:int = 0;
         var _loc7_:int = 0;
         var _loc8_:int = 0;
         var _loc11_:int = 0;
         var _loc12_:int = 0;
         var _loc13_:int = 0;
         var _loc14_:int = 0;
         var _loc3_:NodeRect = helperRect;
         _loc3_.score1 = int.MAX_VALUE;
         _loc3_.score2 = 0;
         var _loc9_:int = this.freeRectangles.length;
         var _loc10_:int = 0;
         while(_loc10_ < _loc9_)
         {
            _loc4_ = this.freeRectangles[_loc10_];
            if(_loc4_.width >= param1 && _loc4_.height >= param2)
            {
               _loc5_ = Math.abs(_loc4_.width - param1);
               _loc6_ = Math.abs(_loc4_.height - param2);
               _loc7_ = Math.min(_loc5_,_loc6_);
               _loc8_ = Math.max(_loc5_,_loc6_);
               if(_loc7_ < _loc3_.score1 || _loc7_ == _loc3_.score1 && _loc8_ < _loc3_.score2)
               {
                  _loc3_.x = _loc4_.x;
                  _loc3_.y = _loc4_.y;
                  _loc3_.width = param1;
                  _loc3_.height = param2;
                  _loc3_.score1 = _loc7_;
                  _loc3_.score2 = _loc8_;
                  _loc3_.rotated = false;
               }
            }
            if(this.allowRotations && _loc4_.width >= param2 && _loc4_.height >= param1)
            {
               _loc11_ = Math.abs(_loc4_.width - param2);
               _loc12_ = Math.abs(_loc4_.height - param1);
               _loc13_ = Math.min(_loc11_,_loc12_);
               _loc14_ = Math.max(_loc11_,_loc12_);
               if(_loc13_ < _loc3_.score1 || _loc13_ == _loc3_.score1 && _loc14_ < _loc3_.score2)
               {
                  _loc3_.x = _loc4_.x;
                  _loc3_.y = _loc4_.y;
                  _loc3_.width = param2;
                  _loc3_.height = param1;
                  _loc3_.score1 = _loc13_;
                  _loc3_.score2 = _loc14_;
                  _loc3_.rotated = true;
               }
            }
            _loc10_++;
         }
         return _loc3_;
      }
      
      private function findPositionForNewNodeBestLongSideFit(param1:int, param2:int) : NodeRect
      {
         var _loc4_:NodeRect = null;
         var _loc5_:int = 0;
         var _loc6_:int = 0;
         var _loc7_:int = 0;
         var _loc8_:int = 0;
         var _loc3_:NodeRect = helperRect;
         _loc3_.score1 = 0;
         _loc3_.score2 = int.MAX_VALUE;
         var _loc9_:int = this.freeRectangles.length;
         var _loc10_:int = 0;
         while(_loc10_ < _loc9_)
         {
            _loc4_ = this.freeRectangles[_loc10_];
            if(_loc4_.width >= param1 && _loc4_.height >= param2)
            {
               _loc5_ = Math.abs(_loc4_.width - param1);
               _loc6_ = Math.abs(_loc4_.height - param2);
               _loc7_ = Math.min(_loc5_,_loc6_);
               _loc8_ = Math.max(_loc5_,_loc6_);
               if(_loc8_ < _loc3_.score2 || _loc8_ == _loc3_.score2 && _loc7_ < _loc3_.score1)
               {
                  _loc3_.x = _loc4_.x;
                  _loc3_.y = _loc4_.y;
                  _loc3_.width = param1;
                  _loc3_.height = param2;
                  _loc3_.score1 = _loc7_;
                  _loc3_.score2 = _loc8_;
                  _loc3_.rotated = false;
               }
            }
            if(this.allowRotations && _loc4_.width >= param2 && _loc4_.height >= param1)
            {
               _loc5_ = Math.abs(_loc4_.width - param2);
               _loc6_ = Math.abs(_loc4_.height - param1);
               _loc7_ = Math.min(_loc5_,_loc6_);
               _loc8_ = Math.max(_loc5_,_loc6_);
               if(_loc8_ < _loc3_.score2 || _loc8_ == _loc3_.score2 && _loc7_ < _loc3_.score1)
               {
                  _loc3_.x = _loc4_.x;
                  _loc3_.y = _loc4_.y;
                  _loc3_.width = param2;
                  _loc3_.height = param1;
                  _loc3_.score1 = _loc7_;
                  _loc3_.score2 = _loc8_;
                  _loc3_.rotated = true;
               }
            }
            _loc10_++;
         }
         return _loc3_;
      }
      
      private function findPositionForNewNodeBestAreaFit(param1:int, param2:int) : NodeRect
      {
         var _loc4_:NodeRect = null;
         var _loc5_:int = 0;
         var _loc6_:int = 0;
         var _loc7_:int = 0;
         var _loc8_:int = 0;
         var _loc3_:NodeRect = helperRect;
         _loc3_.score1 = int.MAX_VALUE;
         _loc3_.score2 = 0;
         var _loc9_:int = this.freeRectangles.length;
         var _loc10_:int = 0;
         while(_loc10_ < _loc9_)
         {
            _loc4_ = this.freeRectangles[_loc10_];
            _loc8_ = _loc4_.width * _loc4_.height - param1 * param2;
            if(_loc4_.width >= param1 && _loc4_.height >= param2)
            {
               _loc5_ = Math.abs(_loc4_.width - param1);
               _loc6_ = Math.abs(_loc4_.height - param2);
               _loc7_ = Math.min(_loc5_,_loc6_);
               if(_loc8_ < _loc3_.score1 || _loc8_ == _loc3_.score1 && _loc7_ < _loc3_.score2)
               {
                  _loc3_.x = _loc4_.x;
                  _loc3_.y = _loc4_.y;
                  _loc3_.width = param1;
                  _loc3_.height = param2;
                  _loc3_.score2 = _loc7_;
                  _loc3_.score1 = _loc8_;
                  _loc3_.rotated = false;
               }
            }
            if(this.allowRotations && _loc4_.width >= param2 && _loc4_.height >= param1)
            {
               _loc5_ = Math.abs(_loc4_.width - param2);
               _loc6_ = Math.abs(_loc4_.height - param1);
               _loc7_ = Math.min(_loc5_,_loc6_);
               if(_loc8_ < _loc3_.score1 || _loc8_ == _loc3_.score1 && _loc7_ < _loc3_.score2)
               {
                  _loc3_.x = _loc4_.x;
                  _loc3_.y = _loc4_.y;
                  _loc3_.width = param2;
                  _loc3_.height = param1;
                  _loc3_.score2 = _loc7_;
                  _loc3_.score1 = _loc8_;
                  _loc3_.rotated = true;
               }
            }
            _loc10_++;
         }
         return _loc3_;
      }
      
      private function commonIntervalLength(param1:int, param2:int, param3:int, param4:int) : int
      {
         if(param2 < param3 || param4 < param1)
         {
            return 0;
         }
         return Math.min(param2,param4) - Math.max(param1,param3);
      }
      
      private function contactPointScoreNode(param1:int, param2:int, param3:int, param4:int) : int
      {
         var _loc6_:NodeRect = null;
         var _loc5_:int = 0;
         if(param1 == 0 || param1 + param3 == this.binWidth)
         {
            _loc5_ = _loc5_ + param4;
         }
         if(param2 == 0 || param2 + param4 == this.binHeight)
         {
            _loc5_ = _loc5_ + param3;
         }
         var _loc7_:int = this.usedRectangles.length;
         var _loc8_:int = 0;
         while(_loc8_ < _loc7_)
         {
            _loc6_ = this.usedRectangles[_loc8_];
            if(_loc6_.x == param1 + param3 || _loc6_.x + _loc6_.width == param1)
            {
               _loc5_ = _loc5_ + this.commonIntervalLength(_loc6_.y,_loc6_.y + _loc6_.height,param2,param2 + param4);
            }
            if(_loc6_.y == param2 + param4 || _loc6_.y + _loc6_.height == param2)
            {
               _loc5_ = _loc5_ + this.commonIntervalLength(_loc6_.x,_loc6_.x + _loc6_.width,param1,param1 + param3);
            }
            _loc8_++;
         }
         return _loc5_;
      }
      
      private function findPositionForNewNodeContactPoint(param1:int, param2:int) : NodeRect
      {
         var _loc4_:NodeRect = null;
         var _loc5_:int = 0;
         var _loc3_:NodeRect = helperRect;
         _loc3_.score1 = -1;
         _loc3_.score2 = 0;
         var _loc6_:int = this.freeRectangles.length;
         var _loc7_:int = 0;
         while(_loc7_ < _loc6_)
         {
            _loc4_ = this.freeRectangles[_loc7_];
            if(_loc4_.width >= param1 && _loc4_.height >= param2)
            {
               _loc5_ = this.contactPointScoreNode(_loc4_.x,_loc4_.y,param1,param2);
               if(_loc5_ > _loc3_.score1)
               {
                  _loc3_.x = _loc4_.x;
                  _loc3_.y = _loc4_.y;
                  _loc3_.width = param1;
                  _loc3_.height = param2;
                  _loc3_.score1 = _loc5_;
                  _loc3_.rotated = false;
               }
            }
            if(this.allowRotations && _loc4_.width >= param2 && _loc4_.height >= param1)
            {
               _loc5_ = this.contactPointScoreNode(_loc4_.x,_loc4_.y,param2,param1);
               if(_loc5_ > _loc3_.score1)
               {
                  _loc3_.x = _loc4_.x;
                  _loc3_.y = _loc4_.y;
                  _loc3_.width = param2;
                  _loc3_.height = param1;
                  _loc3_.score1 = _loc5_;
                  _loc3_.rotated = true;
               }
            }
            _loc7_++;
         }
         return _loc3_;
      }
      
      private function splitFreeNode(param1:NodeRect, param2:NodeRect) : Boolean
      {
         var _loc3_:NodeRect = null;
         if(param2.x >= param1.x + param1.width || param2.x + param2.width <= param1.x || param2.y >= param1.y + param1.height || param2.y + param2.height <= param1.y)
         {
            return false;
         }
         if(param2.x < param1.x + param1.width && param2.x + param2.width > param1.x)
         {
            if(param2.y > param1.y && param2.y < param1.y + param1.height)
            {
               _loc3_ = param1.clone();
               _loc3_.height = param2.y - _loc3_.y;
               this.freeRectangles.push(_loc3_);
            }
            if(param2.y + param2.height < param1.y + param1.height)
            {
               _loc3_ = param1.clone();
               _loc3_.y = param2.y + param2.height;
               _loc3_.height = param1.y + param1.height - (param2.y + param2.height);
               this.freeRectangles.push(_loc3_);
            }
         }
         if(param2.y < param1.y + param1.height && param2.y + param2.height > param1.y)
         {
            if(param2.x > param1.x && param2.x < param1.x + param1.width)
            {
               _loc3_ = param1.clone();
               _loc3_.width = param2.x - _loc3_.x;
               this.freeRectangles.push(_loc3_);
            }
            if(param2.x + param2.width < param1.x + param1.width)
            {
               _loc3_ = param1.clone();
               _loc3_.x = param2.x + param2.width;
               _loc3_.width = param1.x + param1.width - (param2.x + param2.width);
               this.freeRectangles.push(_loc3_);
            }
         }
         return true;
      }
      
      private function pruneFreeList() : void
      {
         var _loc3_:int = 0;
         var _loc1_:int = this.freeRectangles.length;
         var _loc2_:int = 0;
         while(_loc2_ < _loc1_)
         {
            _loc3_ = _loc2_ + 1;
            while(_loc3_ < _loc1_)
            {
               if(this.isContainedIn(this.freeRectangles[_loc2_],this.freeRectangles[_loc3_]))
               {
                  this.freeRectangles.splice(_loc2_,1);
                  _loc1_--;
                  break;
               }
               if(this.isContainedIn(this.freeRectangles[_loc3_],this.freeRectangles[_loc2_]))
               {
                  this.freeRectangles.splice(_loc3_,1);
                  _loc1_--;
               }
               _loc3_++;
            }
            _loc2_++;
         }
      }
      
      private function isContainedIn(param1:NodeRect, param2:NodeRect) : Boolean
      {
         return param1.x >= param2.x && param1.y >= param2.y && param1.x + param1.width <= param2.x + param2.width && param1.y + param1.height <= param2.y + param2.height;
      }
   }
}
